#include "helper.h"
#include "monitor.h"
#include "reg.h"

extern uint32_t instr; // 外部变量，表示当前指令
extern char assembly[80]; // 外部变量，存储指令的汇编表示

/* 解码 I 型指令，带有无符号立即数 */
static void decode_imm_type(uint32_t instr) {

	// 设置操作数1的类型为寄存器
	op_src1->type = OP_TYPE_REG;
	// 从指令中提取源寄存器1的索引
	op_src1->reg = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	// 获取操作数1的值
	op_src1->val = reg_w(op_src1->reg);
	
	// 设置操作数2的类型为立即数
	op_src2->type = OP_TYPE_IMM;
	// 从指令中提取立即数
	op_src2->imm = instr & IMM_MASK;
	// 操作数2的值为立即数
	op_src2->val = op_src2->imm;

	// 设置目标操作数的类型为寄存器
	op_dest->type = OP_TYPE_REG;
	// 从指令中提取目标寄存器的索引
	op_dest->reg = (instr & RT_MASK) >> (IMM_SIZE);
}


make_helper(lui) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = (op_src2->val << 16);
	sprintf(assembly, "lui   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
}

make_helper(ori) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = op_src1->val | op_src2->val;
	sprintf(assembly, "ori   %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
}

make_helper(andi) {

	decode_imm_type(instr);
	reg_w(op_dest->reg) = op_src1->val & op_src2->val;
	sprintf(assembly, "andi  %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
}

make_helper(addiu)
{
	decode_imm_type(instr);
	// sign_extend
	int imm;
	if (op_src2->val & 0x8000)
	{
		imm = (0xFFFF << 16) | op_src2->val;
	}
	else
	{
		imm = op_src2->val;
	}
	reg_w(op_dest->reg) = imm + op_src1->val;
	sprintf(assembly, "addiu %s, %s, 0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->val);
}

make_helper(beq)
{
	decode_imm_type(instr);
	if (reg_w(op_dest->reg) == reg_w(op_src1->reg))
	{
		int imm;
		if (op_src2->val & 0x8000)
		{
			imm = (0xFFFF << 16) | op_src2->val;
		}
		else
		{
			imm = op_src2->val;
		}
		uint32_t addr = (int)cpu.pc + (imm << 2);
		cpu.pc = addr;
	}
	sprintf(assembly, "beq %s, %s, 0x%04x", REG_NAME(op_src1->reg), REG_NAME(op_dest->reg), op_src2->val);
}

make_helper(bne)
{
	decode_imm_type(instr);
	if (reg_w(op_dest->reg) != reg_w(op_src1->reg))
	{
		int imm;
		if (op_src2->val & 0x8000)
		{
			imm = (0xFFFF << 16) | op_src2->val;
		}
		else
		{
			imm = op_src2->val;
		}
		uint32_t addr = (int)cpu.pc + (imm << 2);
		cpu.pc = addr;
	}
	sprintf(assembly, "bne   %s, %s, 0x%04x", REG_NAME(op_src1->reg), REG_NAME(op_dest->reg), op_src2->val);
}

make_helper(blez)
{
	decode_imm_type(instr);
	if ((int)op_src1->val <= 0)
	{
		int imm;
		if (op_src2->val & 0x8000)
		{
			imm = (0xFFFF << 16) | op_src2->val;
		}
		else
		{
			imm = op_src2->val;
		}
		uint32_t addr = (int)cpu.pc + (imm << 2);
		cpu.pc = addr;
	}
	sprintf(assembly, "blez %s, 0x%04x", REG_NAME(op_src1->reg), op_src2->val);
}

make_helper(lb)
{
	decode_imm_type(instr);
	int imm;
	if (op_src2->val & 0x8000)
	{
		imm = (0xFFFF << 16) | op_src2->val;
	}
	else
	{
		imm = op_src2->val;
	}
	uint32_t addr = imm + (uint32_t)op_src1->val;
	uint32_t val = mem_read(addr, 1);
	if (val & 0x80)
	{
		val = (0xFFFFFF << 8) | val;
	}
	reg_w(op_dest->reg) = val;
	sprintf(assembly, "lb  %s, 0x%08x(%s)", REG_NAME(op_dest->reg), op_src2->val, REG_NAME(op_src1->reg));
}

make_helper(lw)
{
	decode_imm_type(instr);
	int imm;
	if (op_src2->val & 0x8000)
	{
		imm = (0xFFFF << 16) | op_src2->val;
	}
	else
	{
		imm = op_src2->val;
	}
	uint32_t addr = imm + (uint32_t)op_src1->val;
	if (addr & 0x3)
	{
		// TODO exception-地址错异常
		if (cpu.cp0.status.EXL == 0)
		{
			cpu.cp0.EPC = cpu.pc;
			cpu.cp0.BadVAddr = addr; // 虚拟地址
			cpu.pc = TRAP_ADDR;
			cpu.cp0.cause.ExcCode = AdEL;
			cpu.cp0.status.EXL = 1;
		}
	}
	else
	{
		uint32_t val = mem_read(addr, 4);
		reg_w(op_dest->reg) = val;
	}
	sprintf(assembly, "lw  %s, 0x%08x(%s)", REG_NAME(op_dest->reg), op_src2->val, REG_NAME(op_src1->reg));
}

make_helper(sb)
{
	decode_imm_type(instr);
	int imm;
	if (op_src2->val & 0x8000)
	{
		imm = (0xFFFF << 16) | op_src2->val;
	}
	else
	{
		imm = op_src2->val;
	}
	uint32_t addr = imm + (uint32_t)op_src1->val;
	mem_write(addr, 1, reg_w(op_dest->reg));
	sprintf(assembly, "sb  %s, 0x%08x(%s)", REG_NAME(op_dest->reg), op_src2->val, REG_NAME(op_src1->reg));
}

make_helper(sw)
{
	decode_imm_type(instr);
	int imm;
	if (op_src2->val & 0x8000)
	{
		imm = (0xFFFF << 16) | op_src2->val;
	}
	else
	{
		imm = op_src2->val;
	}
	uint32_t addr = imm + (uint32_t)op_src1->val;
	if (addr & 0x3)
	{
		// TODO exception-地址错异常
		if (cpu.cp0.status.EXL == 0)
		{
			cpu.cp0.EPC = cpu.pc;	 // 物理地址
			cpu.cp0.BadVAddr = addr; // 虚拟地址
			cpu.pc = TRAP_ADDR;
			cpu.cp0.cause.ExcCode = AdES;
			cpu.cp0.status.EXL = 1;
		}
	}
	else
	{
		mem_write(addr, 4, reg_w(op_dest->reg));
	}
	sprintf(assembly, "sw  %s, 0x%08x(%s)", REG_NAME(op_dest->reg), op_src2->val, REG_NAME(op_src1->reg));
}